package com.handydata.antifraud.ws.dap;

import java.nio.charset.Charset;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.Base64Utils;

import com.alibaba.fastjson.JSONObject;
import com.handydata.antifraud.bo.datacalllogs.DataCallLogsReqBO;
import com.handydata.antifraud.common.exception.WsInvokeException;
import com.handydata.antifraud.service.DataCallLogsService;
import com.handydata.antifraud.to.WsReqTO;
import com.handydata.antifraud.to.WsResTO;
import com.handydata.antifraud.util.HttpUtil;
import com.handydata.antifraud.ws.AbstractWsService;
import com.handydata.antifraud.ws.EncryInfo;
import com.handydata.common.util.crypto.CryptoToStringUtil;

/**
 * 数据平台ws调用服务抽象类
 * @param <T>
 * @param <R>
 */
@SuppressWarnings("rawtypes")
public abstract class DataPlatformWsService<T extends WsReqTO, R extends WsResTO> extends AbstractWsService {

	@Autowired
	protected EncryInfo encryInfo;
	@Autowired
	private DataCallLogsService dataCallLogsService;


	//接口调用超时时间，单位：秒
	@Value("${ws.http.invocation.timeout:5000}")
	private int invokeTimeout;

	protected String dataType;

	private static final Charset UTF8 = Charset.defaultCharset();

	private static final Logger logger = LoggerFactory.getLogger(DataPlatformWsService.class);
	/**
	 * 创建请求报文
	 * @param req
	 * @return
	 */
	protected abstract String createQueryXml(T req) throws WsInvokeException;
	
	/**
	 * 解析结果并保存
	 * @param resultXml
	 * @param orderId
	 * @param createDate
	 */
	protected abstract R parseResultAndSave(String resultXml, String orderId, Date createDate) ;
	/**
	 *
	 * @param req
	 * @return
	 * @throws Exception
	 */
	protected R executeQuery(T req) throws WsInvokeException {
		try {
			long tStart=System.currentTimeMillis();
			String mkey = UUID.randomUUID().toString();
			// 加密报文体格式：BASE64(商户号)| BASE64(RSA(报文加密密钥))| BASE64(3DES(报文原文))
			String strKey = CryptoToStringUtil.encryptByPubKeyToBase64(mkey, encryInfo.getPublicKey());
	
			String xml = createQueryXml(req).replace("null", "");
			String strxml = CryptoToStringUtil.encryptDESToBase64(xml, mkey);
	
			String returnXml = Base64Utils.encodeToString(encryInfo.getChannelId().getBytes(UTF8)) + "|" + strKey + "|" + strxml;

			long queryStart=System.currentTimeMillis();
			logger.info("请求:dataplatform.platform-url = {}", encryInfo.getPlatformUrl());
			String reutrnResult = HttpUtil.sendDataByPost(encryInfo.getPlatformUrl(), returnXml,invokeTimeout);
			long queryElapse=System.currentTimeMillis()-queryStart;

			if(logger.isDebugEnabled()) {
				logger.debug(String.format("Executed result from Dataplatform is  %s ", reutrnResult));
			}
			String[] xmlArr = reutrnResult.split("\\|");
			
			if(xmlArr.length == 3) {
				if ("1".equals(xmlArr[0])) {
					String tradeXml = CryptoToStringUtil.decryptDESFromBase64(xmlArr[1], mkey);
					R obj= handleCorrectResponse(tradeXml, req.getOrderId(), req.getCreateDate());
					long tatalElapsed=System.currentTimeMillis()-tStart;
					CompletableFuture.runAsync(()->insertLog(req,tStart,tatalElapsed,queryElapse,tradeXml));
					return obj;
				} else {
					String msg = handleWrongResponse(xmlArr, req.getOrderId());
					long tatalElapsed=System.currentTimeMillis()-tStart;
					CompletableFuture.runAsync(()->insertLog(req,tStart,tatalElapsed,queryElapse,msg));
					throw new WsInvokeException(msg);
				}
			} else {
				logger.warn(String.format("Query by req: %s then the result: %s which got is in wrong pattern", req, reutrnResult));
				throw new WsInvokeException("Ws result pattern is wrong  dataOrderId: " + req.getQueryXml().substring(req.getQueryXml().indexOf("<channelOrderId>"), req.getQueryXml().indexOf("</channelOrderId>")));
			}
		} catch(Exception e) {
			logger.error("执行外部数据源异常: {} 请求报文: {} 反欺诈订单号: {} ", e, req.getQueryXml(), req.getOrderId());
			throw new WsInvokeException("Invoke ws get into error: dataOrderId: " + req.getQueryXml().substring(req.getQueryXml().indexOf("<channelOrderId>"), req.getQueryXml().indexOf("</channelOrderId>")), e);
		}
	}
	private void insertLog(T req,Long start,Long tatalElapsed,Long queryElapse,String resultXml){
		try {
			logger.info(String.format("执行外部数据源：%s 接口返回原始报文: %s", this.dataType , resultXml));
			if (!"25".equals(this.dataType)) {
				DataCallLogsReqBO dataCallLogsReqBO = new DataCallLogsReqBO();
				dataCallLogsReqBO.setId(UUID.randomUUID().toString().replace("-", ""));
				dataCallLogsReqBO.setParams(JSONObject.toJSONString(req));
				dataCallLogsReqBO.setElapsed(tatalElapsed.intValue());
				dataCallLogsReqBO.setWselapsed(queryElapse.intValue());
				dataCallLogsReqBO.setDataType(this.dataType);
				if ("13".equals(this.dataType) && !"系统异常".equals(resultXml)) {
					Document doc = DocumentHelper.parseText(resultXml);
					Element rootElt = doc.getRootElement(); // 获取根节点
					String resCode = rootElt.elementTextTrim("respCode"); // 拿到subatm节点下的子节点respCode值
					String resMsg = rootElt.elementTextTrim("respDesc"); 
					dataCallLogsReqBO.setResCode(resCode);
					dataCallLogsReqBO.setResMsg(resMsg);
				} else if ("系统异常".equals(resultXml)) {
					dataCallLogsReqBO.setResCode("9999");
					dataCallLogsReqBO.setResMsg(resultXml);
				} else {
					JSONObject jasonObject = JSONObject.parseObject(resultXml); 
					dataCallLogsReqBO.setResCode(jasonObject.getString("resCode"));
					dataCallLogsReqBO.setResMsg(jasonObject.getString("resMsg"));
				}
				dataCallLogsReqBO.setResult(resultXml);
				dataCallLogsReqBO.setOrderId(req.getOrderId());
				dataCallLogsReqBO.setUpdateTime(new Date());
				dataCallLogsReqBO.setCreateTime(new Date(start));
				dataCallLogsService.createDataCallLogs(dataCallLogsReqBO);
			}
		} catch (DocumentException e) {
			logger.error("数据源查询接口返回报文解析或数据入库异常：{}", e);
		}
	}
	/**
	 * 处理正确响应
	 * @param
	 * @param
	 * @param orderId
	 * @param createDate
	 * @return
	 * @throws Exception
	 */
	protected R handleCorrectResponse(String tradeXml, String orderId, Date createDate) throws Exception {
		return parseResultAndSave(tradeXml, orderId, createDate);
	}
	/**
	 * 处理错误响应
	 * @param xmlArr
	 * @param orderId
	 * @return
	 * @throws Exception
	 */
	protected String handleWrongResponse(String[] xmlArr, String orderId) {
		if ("0".equals(xmlArr[0]) && "1001".equals(xmlArr[1])) {
			logger.info(String.format("%s orderId is %s response code is 1001", this.getClass().getSimpleName(),orderId));
			return xmlArr[1];
		} else {
			String msg = new String(Base64Utils.decodeFromString(xmlArr[2]), UTF8);
			logger.info(String.format("%s orderId is %s response msg is %s", this.getClass().getSimpleName(), orderId, msg));
			return msg;
		} 
	}
}
